﻿using System.Collections.ObjectModel;
using System.Web.Security;
using Monitor.Models.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Monitor.Models.Json;
using Monitor.Models.JsonModel;
using Monitor.Repository.UserRepository.LinqModel;
using WebMatrix.WebData;

namespace Monitor.Repository.UserRepository
{
    #region UserRep
    /// <summary>
    /// Класс для работы с базой конфигурации пользователей в системе
    /// </summary>
    public class UserRepository : IUserRepository
    {

        #region конфигурация базы
        /// <summary>
        /// модель EF БД пользователей
        /// </summary>
        private UserBaseEntities _userBaseEntities = new UserBaseEntities();
        /// <summary>
        /// освобождение ресурсов
        /// </summary>
        public void Dispose()
        {
            _userBaseEntities.Dispose();
        }

        #endregion

        #region проверка на то, что пользователь с выбранным логин в базе отсутствует
        /// <summary>
        /// Проверка на то, что выбранный логин свободен в БД
        /// </summary>
        /// <param name="login">логин пользователя</param>
        /// <returns>true - свободен, false - занят</returns>
        public bool FreeLogin(string login)
        {
            return !_userBaseEntities.Users.Any(n => n.UserName == login);
        }

        #endregion

        #region извлечение пользователей
        /// <summary>
        /// Вовзращает IEnumerable всех пользователей в системе для последующих запросов посредством LinQ
        /// </summary>
        IEnumerable<User> IUserRepository.Users
        {
            get { return _userBaseEntities.Users; }
        }
        /// <summary>
        /// Извлекает пользователя по индификатору
        /// </summary>
        /// <param name="userId">индификатор пользователя</param>
        /// <returns>если пользователь есть в базе, он вернет объект-пользователь</returns>
        public User GetUser(int userId)
        {
            return _userBaseEntities.Users.Find(userId);
        }
        /// <summary>
        /// Возвращает пользователя по его логину
        /// </summary>
        /// <param name="name">логин</param>
        /// <returns>если пользователь есть в базе, он вернет объект-пользователь</returns>
        public User GetUser(string name)
        {
            return _userBaseEntities.Users.FirstOrDefault(n=>n.UserName==name);
        }

        #endregion

        #region удаление машин по FK
        /// <summary>
        /// Удаляет машину из БД пользователей
        /// </summary>
        /// <param name="carFK">индификатор машины из БД сервера</param>
        /// <returns>возвращает истина, если удаление произошло успешно</returns>
        public bool CarConfigRemove(Int64 carFK)
        {
            try
            {
                foreach (Car car in _userBaseEntities.Cars.Where(n => n.Fk_CarId == carFK))
                {
                    car.Areas.Clear();
                    car.CarGroups.Clear();
                    _userBaseEntities.Cars.Remove(car);
                }
                SaveChanges();
            }
            catch (Exception)
            {
                return false;
            }
            return true;
        }

        #endregion

        #region добавление нового пользователя
        /// <summary>
        /// добавление нового пользователя в систему
        /// </summary>
        /// <param name="model">модель нового пользователя</param>
        /// <returns>истина, если удаление успешно, ложь если модель неправильная или занят логин, в случае ошибки действия откатываются и передается ошибка</returns>
        public bool AddNewUser(Models.Json.UserInputModel model)
        {
            if(model.IsValid&&FreeLogin(model.Login))
            {
                WebSecurity.CreateUserAndAccount(model.Login, model.Password);
                try
                {
                    if (Roles.GetAllRoles().Contains(model.Role))
                        Roles.AddUserToRole(model.Login, model.Role);
                    User newUser = GetUser(model.Login);
                    if(newUser!=null)
                    {
                        newUser.Name = model.UserName;
                        newUser.Family = model.Family;
                        foreach (long carId in model.ListOfCar)
                        {
                            Car curCar = new Car
                            {
                                Fk_CarId = carId,
                                UserId = newUser.UserId
                            };
                            newUser.Cars.Add(curCar);
                        }
                        SaveChanges();
                        return true; 
                    }
                }
                catch (Exception)
                {
                    DeleteUser(model.Login);
                    throw;
                }
            }
            return false;
        }

        #endregion
        
        #region обновление пользователя
        /// <summary>
        /// обновление пользователя
        /// </summary>
        /// <param name="model">модель пользователя</param>
        /// <returns>истина - если успешно, ложь - если модель неправильная или такого пользователя уже нет. Ошибка передается наверх</returns>
        public bool UpdateUser(Models.Json.UserInputModel model)
        {
            //если модель достоверная и логин свободный
            if (model.IsValid && !FreeLogin(model.Login))
            {
                //поиск записи пользователя
                User user = GetUser(model.Login);
                //обновление роли
                if (Roles.GetAllRoles().Contains(model.Role))
                {
                    Roles.RemoveUserFromRoles(model.Login, Roles.GetRolesForUser(model.Login));
                    Roles.AddUserToRole(model.Login, model.Role);
                }
                //обновление имени
                user.Name = model.UserName;
                //обновление фамилии
                user.Family = model.Family;
                //получение списка новых машин
                Int64[] newElement = model.ListOfCar.Except<Int64>(user.Cars.Select(n => n.Fk_CarId)).ToArray();
                //получение списка удаляемых машин
                Int64[] removeElement = user.Cars.Select(n => n.Fk_CarId).Except<Int64>(model.ListOfCar).ToArray();
                //добавление новых машин к пользователю
                foreach (long carId in newElement)
                {
                    Car car = new Car
                                  {
                                      Fk_CarId = carId,
                                      UserId = user.UserId

                                  };
                    user.Cars.Add(car);
                }
                //удаление машин
                foreach (long carId in removeElement)
                {
                    Car car = user.Cars.FirstOrDefault(n => n.Fk_CarId == carId);
                    if(car!=null)
                    {
                        car.CarGroups.Clear();
                        car.Areas.Clear();
                        user.Cars.Remove(car);
                    }
                }
                //сохранение транзацкии
                _userBaseEntities.SaveChanges();
                return true;
            }
            return false;
        }
        
        #endregion

        #region удаление пользователя по логину

        /// <summary>
        /// удаление пользователя по его логину
        /// </summary>
        /// <param name="login">логин</param>
        public void DeleteUser(string login)
        {
            if (!FreeLogin(login))                                          //пользователь в системе существует
            {
                string[] currentRolse = Roles.GetRolesForUser(login);       //извлечение списка ролей
                if(currentRolse!=null&&currentRolse.Any())                  //пользователю назначены роли
                    Roles.RemoveUserFromRoles(login, currentRolse);         //удаление ролей
                User removeUser = GetUser(login);                           //извлечение пользователя по его логину
                foreach (CarGroup carGroup in removeUser.CarGroups)
                {
                    carGroup.Cars.Clear();                                  //удаление свзяей в таблицах групп (не до конца каскадное удаление настроено)
                }
                foreach (Area area in removeUser.Areas)
                {
                    area.Cars.Clear();                                      //очистка машинок в зонах
                    area.AreaGroups.Clear();                                //очистка связко с группами зон
                }
                SaveChanges();                                              //применение изменений
                ((SimpleMembershipProvider)Membership.Provider).DeleteUser(login, true);//удаление провайдером безопастности
            }
        }

        #endregion

        #region Сохранение изменений
        /// <summary>
        /// применение изменений
        /// </summary>
        void SaveChanges()
        {
            _userBaseEntities.SaveChanges();
        }

        #endregion

        #region Работа с деревом машинок

        /// <summary>
        /// Перемещает машинку из одного каталога в другой
        /// </summary>
        /// <param name="userName">Имя пользователя</param>
        /// <param name="carId">индификатор машины</param>
        /// <param name="oldGroupId">предыдущая группа (-1) - из списка</param>
        /// <param name="newGroupId">индификатор новой группы</param>
        /// <returns>истина - если успешно осуществлено перемещение, ложь - недостоверные данные</returns>
        public bool MoveCar(string userName, long carId, long newGroupId, long oldGroupId = -1)
        {
            User currentUser = GetUser(userName);
            if (currentUser != null)
            {
                Car selectedCar = currentUser.Cars.FirstOrDefault(n => n.CarId == carId);
                if (selectedCar != null)
                {
                    CarGroup selectedGroup = currentUser.CarGroups.FirstOrDefault(n => n.GroupCarId == newGroupId);
                    if (selectedGroup != null && !selectedGroup.Cars.Contains(selectedCar))
                    {
                        selectedGroup.Cars.Add(selectedCar);
                        if(oldGroupId>0)
                        {
                            CarGroup oldGroup = selectedCar.CarGroups.FirstOrDefault(n => n.GroupCarId == oldGroupId);
                            if(oldGroup!=null)
                            {
                                selectedCar.CarGroups.Remove(oldGroup);
                            }
                        }
                        SaveChanges();
                        return true;
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// Перемещение группы из одной группы в другую
        /// </summary>
        /// <param name="userName">логин пользователя</param>
        /// <param name="groupId">индификатор группы</param>
        /// <param name="newParentId">индификатор группы-родителя</param>
        /// <returns>истина - успешно осущствлена операция, ложь - неполные данные</returns>
        public bool MoveGroup(string userName, long groupId, long newParentId)
        {
            User currentUser = GetUser(userName);
            if(currentUser!=null)
            {
                CarGroup currentGroup = currentUser.CarGroups.FirstOrDefault(n => n.GroupCarId == groupId);
                if(currentGroup!=null)
                {
                    CarGroup newParent = currentUser.CarGroups.FirstOrDefault(n => n.GroupCarId == newParentId);
                    if(newParent!=null)
                    {
                        currentGroup.ParentId = newParent.GroupCarId;
                        SaveChanges();
                        return true;
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// Добавление новой машины
        /// </summary>
        /// <param name="userName">логин пользователя</param>
        /// <param name="fk_Id">ключ-индификатор в БД</param>
        /// <param name="groupId">индификатор группы для вставки</param>
        /// <returns>объект-машина данного пользователя</returns>
        public Car AddNewCar(string userName, long fk_Id, long groupId)
        {
            User currentUser = GetUser(userName);
            if(currentUser!=null)
            {
                CarGroup group = currentUser.CarGroups.FirstOrDefault(n => n.GroupCarId == groupId);
                if(group!=null)
                {
                    Car selectedCar = currentUser.Cars.FirstOrDefault(n => n.Fk_CarId == fk_Id);
                    if (selectedCar==null)//если машины не найдено в БД, то добавить
                    {
                        selectedCar = new Car()
                                          {
                                              Fk_CarId = fk_Id,
                                              IconClass = "else"
                                          };
                        currentUser.Cars.Add(selectedCar);
                        SaveChanges();
                    }
                    group.Cars.Add(selectedCar);
                    SaveChanges();
                    return selectedCar;
                }
            }
            return null;
        }


        /// <summary>
        /// Удаляет машину из дерева машин выбранного пользователя у выбранной группы
        /// </summary>
        /// <param name="userName">имя пользователя</param>
        /// <param name="carId">индификатор машины</param>
        /// <param name="groupId">индификатор группы</param>
        /// <returns>истина - удаление успешно прошло, ложь - не все данные</returns>
        public bool DeleteCarFromGroup(string userName, long carId, long groupId)
        {
            User user = GetUser(userName);
            if (user != null)
            {
                Car selectedCar = user.Cars.FirstOrDefault(n => n.CarId == carId);
                if (selectedCar != null)
                {
                    CarGroup selectedGroup = selectedCar.CarGroups.FirstOrDefault(n => n.GroupCarId == groupId);
                    if (selectedGroup != null)
                    {
                        selectedCar.CarGroups.Remove(selectedGroup);
                        if (selectedCar.CarGroups.Count == 0)
                        {
                            user.Cars.Remove(selectedCar);
                            _userBaseEntities.Cars.Remove(selectedCar);
                        }
                        SaveChanges();
                        return true;
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// Удаляет машину у пользователя
        /// </summary>
        /// <param name="userName">логин пользователя</param>
        /// <param name="groupId">индификатор группы</param>
        /// <returns>истина - удаление успешно, ложь - не все данные</returns>
        public bool DeleteGroup(string userName, long groupId)
        {
            User user = GetUser(userName);
            if (user != null)
            {
                CarGroup selectedGroup = user.CarGroups.FirstOrDefault(n => n.GroupCarId == groupId);
                if (selectedGroup != null)
                {
                    selectedGroup.Cars.Clear();
                    user.CarGroups.Remove(selectedGroup);
                    SaveChanges();
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Добавление новой группы
        /// </summary>
        /// <param name="userName">имя пользователя</param>
        /// <param name="groupLabel">название группы</param>
        /// <param name="parentId">индификатор родителя</param>
        /// <returns>истина - добавить удалось, ложь - не все данные</returns>
        public bool AddNewGroup(string userName, string groupLabel, long parentId)
        {
            User user = GetUser(userName);
            if (user != null)
            {
                if (parentId != -1 && user.CarGroups.Any(n => n.GroupCarId == parentId) || parentId == -1)
                {
                    CarGroup newGroup = new CarGroup()
                                            {
                                                UserId = user.UserId,
                                                ParentId = parentId,
                                                FullName = groupLabel,
                                                IconClass = "group"
                                            };
                    user.CarGroups.Add(newGroup);
                    SaveChanges();
                    return true;
                }
            }
            return false;
        }

        #endregion

        #region редактирование дерева зон

        /// <summary>
        /// Добавление новой группы зон
        /// </summary>
        /// <param name="userName">логин</param>
        /// <param name="label">имя зоны</param>
        /// <param name="parentId">индификатор родителя</param>
        /// <returns>истина - вставка осуществлена, ложь - неполные данные</returns>
        public bool AddNewZoneGroup(string userName, string label, long parentId)
        {
            User currentUser = GetUser(userName);
            if (currentUser != null)
            {
                if (parentId ==-1|| currentUser.AreaGroups.Any(n => n.GroupAreaId == parentId))
                {
                    AreaGroup newGroup=new AreaGroup()
                                           {
                                               FullName = label,
                                               ParentId = parentId
                                           };
                    currentUser.AreaGroups.Add(newGroup);
                    SaveChanges();
                    return true;

                }
            }
            return false;
        }

        public bool MoveZoneGroup(string userName, long zoneGroupId, long parentId)
        {
            if (zoneGroupId != parentId)
            {
                User currentUser = GetUser(userName);
                if (currentUser != null)
                {
                    AreaGroup areaGroup = currentUser.AreaGroups.FirstOrDefault(n => n.GroupAreaId == zoneGroupId);
                    if (areaGroup != null)
                    {
                        if (parentId == -1 || currentUser.AreaGroups.Any(n => n.GroupAreaId == parentId))
                        {
                            areaGroup.ParentId = parentId;
                            SaveChanges();
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// Удаление группы зон у пользователя
        /// </summary>
        /// <param name="userName">имя пользователя</param>
        /// <param name="id">индификатор зоны</param>
        /// <returns>истина - удаление прошло, ложь - уже отсутствует</returns>
        public bool RemoveZoneGroup(string userName, long id)
        {
            User currentUser = GetUser(userName);
            if (currentUser != null)
            {
                AreaGroup selectedGroup = currentUser.AreaGroups.FirstOrDefault(n => n.GroupAreaId == id);
                if (selectedGroup != null)
                {
                    selectedGroup.Areas.Clear();
                    currentUser.AreaGroups.Remove(selectedGroup);
                    _userBaseEntities.AreaGroups.Remove(selectedGroup);
                    SaveChanges();
                    return true;
                }
            }
            return false;
        }


        /// <summary>
        /// Добавить новую зону
        /// </summary>
        /// <param name="userName">имя пользователя</param>
        /// <param name="masterId">индификатор группы</param>
        /// <param name="model">модель зоны</param>
        /// <returns></returns>
        public bool AddNewZone(string userName, long masterId, NewZoneItem model)
        {
            User curUser = GetUser(userName);
            if (curUser != null)
            {
                ICollection<Car> cars=new Collection<Car>();
                if (model.CarId != null)
                    cars =
                        new Collection<Car>(curUser.Cars.Join(model.CarId, n => n.CarId, m => m, (n, m) => n).ToList());
                AreaGroup areaGroup = curUser.AreaGroups.FirstOrDefault(n => n.GroupAreaId == masterId);
                if (areaGroup != null)
                {
                    Area area = new Area()
                                    {
                                        Cars = cars,
                                        FullName = model.Label,
                                        Coords = model.Coord,
                                        UserId = curUser.UserId
                                    };
                    areaGroup.Areas.Add(area);
                    SaveChanges();
                    return true;
                }
            }
            return false;
        }
       
        /// <summary>
        /// Удаление зоны у пользователя
        /// </summary>
        /// <param name="userName">логин пользователя</param>
        /// <param name="id">индификатор зоны</param>
        /// <returns>истина - операция успешно пройдена, ложь - неполные данные</returns>
        public bool RemoveZone(string userName, long id)
        {
            User user = GetUser(userName);
            if (user != null)
            {
                Area curArea = user.Areas.FirstOrDefault(n => n.AreaId == id);
                if (curArea != null)
                {
                    curArea.AreaGroups.Clear();
                    curArea.Cars.Clear();
                    user.Areas.Remove(curArea);
                    _userBaseEntities.Areas.Remove(curArea);
                    SaveChanges();
                    return true;
                }
            }
            return false;
        }
        
        /// <summary>
        /// Перемещение зоны
        /// </summary>
        /// <param name="userName">имя пользователя</param>
        /// <param name="zoneId">индификатор зоны</param>
        /// <param name="newGroupeId">индификатор новой группы</param>
        /// <returns>истина - данные все, ложь - неполные данные</returns>
        public bool MoveZone(string userName, long zoneId, long newGroupeId)
        {
            User user = GetUser(userName);
            if (user != null)
            {
                Area area = user.Areas.FirstOrDefault(n => n.AreaId == zoneId);
                if (area != null)
                {
                    AreaGroup areaGroup = user.AreaGroups.FirstOrDefault(n => n.GroupAreaId == newGroupeId);
                    if (areaGroup != null)
                    {
                        area.AreaGroups.Clear();
                        areaGroup.Areas.Add(area);
                        SaveChanges();
                        return true;
                    }
                    
                }
            }
            return false;
        }

        #endregion

    }
    #endregion
}